package scenery;

import com.crunch.math.Vector4f;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

/**
 * A set of tiles.
 */
public class TileLayer {
	public TileLayer() {
		tiles = new ArrayList<Tile>();
		depth = 0;
		parallaxX = 0;
		parallaxY = 0;
		color = new Vector4f(1.0f, 1.0f, 1.0f, 1.0f);
	}

	public JSONObject save()
			throws JSONException {
		JSONObject rootObj = new JSONObject();

		JSONObject propertiesObj = new JSONObject();
		rootObj.put("properties", propertiesObj);
		propertiesObj.put("depth", getDepth());
		propertiesObj.put("parallax", new JSONArray().put(getParallaxX()).put(getParallaxY()));
		propertiesObj.put("color", new JSONArray().put(color.x()).put(color.y()).put(color.z()).put(color.w()));

		JSONArray tilesArr = new JSONArray();
		rootObj.put("tiles", tilesArr);

		for (Tile tile : tiles) {
			JSONObject tileObj = new JSONObject();
			tilesArr.put(tileObj);

			tileObj.put("x", tile.getX());
			tileObj.put("y", tile.getY());
			tileObj.put("imageX", tile.getImageX());
			tileObj.put("imageY", tile.getImageY());
			tileObj.put("width", tile.getWidth());
			tileObj.put("height", tile.getHeight());
			tileObj.put("frames", tile.getFrames());
			tileObj.put("frameTime", tile.getFrameTime());
			tileObj.put("timeOffset", tile.getTimeOffset());
			tileObj.put("frameOffsetX", tile.getFrameOffsetX());
			tileObj.put("frameOffsetY", tile.getFrameOffsetY());
		}

		return rootObj;
	}

	public void load(JSONObject rootObj)
			throws JSONException {
		JSONObject propertiesObj = rootObj.getJSONObject("properties");
		setDepth(propertiesObj.getInt("depth"));
		JSONArray parallax = propertiesObj.getJSONArray("parallax");
		setParallax(
				(float) parallax.getDouble(0),
				(float) parallax.getDouble(1));
		JSONArray color = propertiesObj.getJSONArray("color");
		setColor(new Vector4f(
				(float) color.getDouble(0),
				(float) color.getDouble(1),
				(float) color.getDouble(2),
				(float) color.getDouble(3)));

		JSONArray tilesArr = rootObj.getJSONArray("tiles");
		for (int i = 0; i < tilesArr.length(); ++i) {
			JSONObject tileObj = tilesArr.getJSONObject(i);

			int x = tileObj.getInt("x");
			int y = tileObj.getInt("y");
			int imageX = tileObj.getInt("imageX");
			int imageY = tileObj.getInt("imageY");
			int width = tileObj.getInt("width");
			int height = tileObj.getInt("height");
			int frames = tileObj.getInt("frames");
			int frameTime = tileObj.getInt("frameTime");
			int timeOffset = tileObj.getInt("timeOffset");
			int frameOffsetX = tileObj.getInt("frameOffsetX");
			int frameOffsetY = tileObj.getInt("frameOffsetY");

			if (imageX < Short.MIN_VALUE || imageX > Short.MAX_VALUE ||
					imageY < Short.MIN_VALUE || imageY > Short.MAX_VALUE ||
					width < Short.MIN_VALUE || width > Short.MAX_VALUE ||
					height < Short.MIN_VALUE || height > Short.MAX_VALUE ||
					frames < Short.MIN_VALUE || frames > Short.MAX_VALUE ||
					frameTime < Short.MIN_VALUE || frameTime > Short.MAX_VALUE ||
					timeOffset < Short.MIN_VALUE || timeOffset > Short.MAX_VALUE ||
					frameOffsetX < Short.MIN_VALUE || frameOffsetX > Short.MAX_VALUE ||
					frameOffsetY < Short.MIN_VALUE || frameOffsetY > Short.MAX_VALUE) {
				throw new JSONException("Invalid tile attribute(s)");
			}

			Tile tile = new Tile();
			tile.setX(x);
			tile.setY(y);
			tile.setImageX((short) imageX);
			tile.setImageY((short) imageY);
			tile.setWidth((short) width);
			tile.setHeight((short) height);
			tile.setFrames((short) frames);
			tile.setFrameTime((short) frameTime);
			tile.setTimeOffset((short) timeOffset);
			tile.setFrameOffsetX((short) frameOffsetX);
			tile.setFrameOffsetY((short) frameOffsetY);
			addTile(tile);
		}
	}

	public static class Tile {
		public Tile() {
			x = 0;
			y = 0;
			imageX = 0;
			imageY = 0;
			width = 0;
			height = 0;
			frames = 1;
			frameTime = 1;
			timeOffset = 0;
			frameOffsetX = 0;
			frameOffsetY = 0;
		}

		public int getX() {
			return x;
		}

		public int getY() {
			return y;
		}

		public short getImageX() {
			return imageX;
		}

		public short getImageY() {
			return imageY;
		}

		public short getWidth() {
			return width;
		}

		public short getHeight() {
			return height;
		}

		public short getFrames() {
			return frames;
		}

		public short getFrameTime() {
			return frameTime;
		}

		public short getTimeOffset() {
			return timeOffset;
		}

		public short getFrameOffsetX() {
			return frameOffsetX;
		}

		public short getFrameOffsetY() {
			return frameOffsetY;
		}

		public void setX(int x) {
			this.x = x;
		}

		public void setY(int y) {
			this.y = y;
		}

		public void setImageX(short imageX) {
			this.imageX = imageX;
		}

		public void setImageY(short imageY) {
			this.imageY = imageY;
		}

		public void setWidth(short width) {
			this.width = width;
		}

		public void setHeight(short height) {
			this.height = height;
		}

		public void setFrames(short frames) {
			this.frames = frames;
		}

		public void setFrameTime(short frameTime) {
			this.frameTime = frameTime;
		}

		public void setTimeOffset(short timeOffset) {
			this.timeOffset = timeOffset;
		}

		public void setFrameOffsetX(short frameOffsetX) {
			this.frameOffsetX = frameOffsetX;
		}

		public void setFrameOffsetY(short frameOffsetY) {
			this.frameOffsetY = frameOffsetY;
		}

		private int x, y;
		private short imageX, imageY;
		private short width, height;
		private short frames;
		private short frameTime;
		private short timeOffset;
		private short frameOffsetX, frameOffsetY;
	}

	public int getTileCount() {
		return tiles.size();
	}

	public int addTile(Tile tile) {
		tiles.add(tile);
		return tiles.size()-1;
	}

	public Tile getTile(int i) {
		return tiles.get(i);
	}

	public void removeTile(int i) {
		tiles.remove(i);
	}

	public int[] getTilesAtPosition(int x, int y) {
		ArrayList<Integer> tileList = new ArrayList<Integer>();
		for (int i = 0; i < tiles.size(); ++i) {
			Tile tile = tiles.get(i);
			if (x >= tile.getX() && y >= tile.getY() &&
					x < tile.getX() + tile.getWidth() && y < tile.getY() + tile.getHeight()) {
				tileList.add(i);
			}
		}
		int[] ret = new int[tileList.size()];
		for (int i = 0; i < tileList.size(); ++i) {
			ret[i] = tileList.get(i);
		}
		return ret;
	}

	public int getDepth() {
		return depth;
	}

	public void setDepth(int depth) {
		this.depth = depth;
	}

	public float getParallaxX() {
		return parallaxX;
	}

	public float getParallaxY() {
		return parallaxY;
	}

	public void setParallax(float parallaxX, float parallaxY) {
		this.parallaxX = parallaxX;
		this.parallaxY = parallaxY;
	}

	public Vector4f getColor() {
		return new Vector4f(color);
	}

	public void setColor(Vector4f color) {
		this.color.set(color);
	}

	private ArrayList<Tile> tiles;
	private int depth;
	private float parallaxX;
	private float parallaxY;
	private Vector4f color;
}
